package store.lunangangster.nodeflow.builder;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import store.lunangangster.nodeflow.exception.NodeBuildException;
import store.lunangangster.nodeflow.rt.FlowBus;
import store.lunangangster.nodeflow.rt.element.Chain;
import store.lunangangster.nodeflow.rt.element.flow.Flow;
import java.util.ArrayList;
import java.util.List;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Chain的构建器
 *
 * @author cwk
 * @version 1.0
 */
public class ChainBuilder {
	private static final Logger log = LoggerFactory.getLogger(ChainBuilder.class);

	private final Chain chain;

	// 该标志位为true，表示不会向FlowBus中添加一个新的Chain，而是找一个相同id的Chain进行属性赋值操作
	private boolean assign = false;

	private ChainBuilder() {
		this.chain = new Chain();
	}

	/**
	 * 构建一个Chain
	 * <p>该方法会将先前添加好的属性作为待构造Chain的属性进行注入，
	 * 调用完成该方法后，会将Chain创建并且加入到{@link FlowBus}中。
	 * <p>当调用了{@link #assign()}方法之后，此方法并不会将Chain加入到{@link FlowBus}中，
	 * 而是会从{@link FlowBus}中找到chainId与之对应的Chain，并且将当前创建好的Chain的属性copy到目的Chain中
	 */
	public void build() {
		// build前的简单检查
		checkBuild();
		if (!assign) {
			// 向FlowBus中添加Chain
			FlowBus.addChain(chain);
		} else {
			// 否则找到目的Chain进行属性赋值操作
			Chain oldChain = FlowBus.getChain(chain.getId());
			BeanUtil.copyProperties(chain, oldChain);
		}
		log.info("chain({}) is added to FlowBus", chain.getId());
	}

	/**
	 * 构建一个Chain
	 * <p>该方法会将先前添加好的属性作为待构造Chain的属性进行注入，
	 * 调用完成该方法后，会将Chain创建并且加入到{@link FlowBus}中。
	 * <p>当调用了{@link #assign()}方法之后，此方法并不会将Chain加入到{@link FlowBus}中，
	 * 而是会从{@link FlowBus}中找到chainId与之对应的Chain，并且将当前创建好的Chain的属性copy到目的Chain中
	 *
	 * @return 构建出的Chain
	 */
	public Chain build(boolean addToFlow) {
		// build前的简单检查
		checkBuild();
		if (!assign) {
			// 向FlowBus中添加Chain
			if (addToFlow) {
				FlowBus.addChain(chain);
			}
		} else {
			// 否则找到目的Chain进行属性赋值操作
			Chain oldChain = FlowBus.getChain(chain.getId());
			BeanUtil.copyProperties(chain, oldChain);
		}
		log.info("chain({}) is added to FlowBus", chain.getId());
		return chain;
	}

	/**
	 * 开始构建Chain
	 * @return 生成的ChainBuilder对象
	 */
	public static ChainBuilder start() {
		return new ChainBuilder();
	}

	/**
	 * 设置正在构建的Chain的chainId
	 * @param id	chainId
	 * @return 生成的ChainBuilder对象
	 */
	public ChainBuilder id(String id) {
		this.chain.setId(id);
		return this;
	}

	/**
	 * 设置正在构建的Chain的flowList
	 * @param flowList	执行流集合
	 * @return 生成的ChainBuilder对象
	 */
	public ChainBuilder flowList(List<Flow> flowList) {
		this.chain.setFlowList(flowList);
		return this;
	}

	/**
	 * 设置正在构建的Chain的构建行为。执行完该方法后的{@link #build()}方法，并不会向{@link FlowBus}中添加
	 * 一个Chain，而是从{@link FlowBus}中找到相同id的{@link Chain}并且将属性进行复制
	 * @return 生成的ChainBuilder对象
	 */
	public ChainBuilder assign() {
		this.assign = true;
		return this;
	}

	/**
	 * {@link #build()}之前的简单检查
	 */
	private void checkBuild() {
		boolean isError = StrUtil.isBlank(chain.getId()) || CollUtil.isEmpty(chain.getFlowList());
		List<String> errorInfos;
		if (!isError) {
			return;
		}
		errorInfos = new ArrayList<>();
		if (StrUtil.isBlank(chain.getId())) {
			errorInfos.add("chain's id is blank");
		}
		if (CollUtil.isEmpty(chain.getFlowList())) {
			errorInfos.add("chain's flowList is null or empty");
		}
		throw new NodeBuildException(CollUtil.join(errorInfos, ", ", "[", "]"));
	}
}
